#include "stdafx.h"

#include "HttpRequest.h"
#include <WinSock.h>
#include <iostream>

#include <sstream>


#include "Encode.h"

#pragma warning(disable : 4996)

#pragma comment(lib, "ws2_32.lib")

size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream);


HttpRequest::HttpRequest(const std::string& ip, int port) : m_ip(ip), m_port(port)
{
}


HttpRequest::~HttpRequest(void)
{
}




int ReadBytes(SOCKET  s,  char  * lpByte, int dwBytes)
{
	struct timeval tv;
	fd_set fdRead;

	//这里有可能是时间较久，需要考虑优化
	tv.tv_sec = 10;
	tv.tv_usec = 0;
	FD_ZERO(&fdRead);
	FD_SET(s, &fdRead);
	
	int nError = select(0, &fdRead, NULL, NULL, &tv);
    if (nError <= 0)
    {
       return nError;
    }

	if (FD_ISSET(s, &fdRead))
	{
        memset(lpByte, 0, dwBytes);
        nError = recv(s, (char*)lpByte, dwBytes, 0);
    }

     return nError;

 }

#define CHUNK_SIZE  4096 * 100

// Http GET请求
int HttpRequest::HttpGet(std::string req, DownloadCallback  callback, void* context)
{
	try
	{
		// 开始进行socket初始化
		WSADATA wData;
		::WSAStartup(MAKEWORD(2, 2), &wData);

		SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0);
		struct sockaddr_in ServerAddr = { 0 };
		ServerAddr.sin_addr.s_addr = inet_addr(m_ip.c_str());
		ServerAddr.sin_port = htons(m_port);
		ServerAddr.sin_family = AF_INET;


		int error_code;
		u_long mode = 1;
		ioctlsocket(clientSocket, FIONBIO, &mode); //控制为非阻塞方式。

		while (1)
		{
			if (connect(clientSocket, (const sockaddr*)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
			{
				error_code = WSAGetLastError();
				if (error_code == WSAEWOULDBLOCK || error_code == WSAEINVAL)
				{
					Sleep(100);
					continue;
				}
				//当连接一次后需要判断是否已经连接.不然会报10056错误
				else if (error_code == WSAEISCONN)
				{
					break;
				}
				else
				{
					std::cout << "connect error code is " << error_code << std::endl;
					closesocket(clientSocket);
					WSACleanup();
					return -1;
				}
			}
			break;
		}

		
		std::string strSend = " HTTP/1.1\r\n"
			"Host: " + m_ip + ":" + std::to_string(m_port) + "\r\n"
			"Accept: */*\r\n\r\n";
		strSend = "GET " + req + strSend;
		// 发送
		error_code = send(clientSocket, strSend.c_str(), strSend.length(), 0);
		if (error_code > 0)
		{
	//		printf("send ok \n");
		}
		else
		{
			printf("send err  %d \n", error_code);
			closesocket(clientSocket);
			return -1;
		}
			 
		char* bufRecv = (char*)malloc(1024 * 1024);
		if (bufRecv == NULL)
		{
			printf("malloc err  \n");
			closesocket(clientSocket);
			return -1;
		}

		int len = 0;
		long count = 0;
		long downloadsize = 0;
		bool  process_http_head = false;
		bool exit_flag = false;
		int index = 0;
		do
		{
			//printf("index %d, len %d, exit_flag %d, count %d, downsize %d \n", index, len, exit_flag, count, downloadsize);
			if (downloadsize > 0 && count + index >= downloadsize)
			{
				len = 0;
				break;
			}

			len = ReadBytes(clientSocket, bufRecv + index, CHUNK_SIZE  - index);
			if (len >= 0)
			{
				//要去掉HTTP头, 可能有会有拆包情况，后续完善
				if (process_http_head == false)
				{
					//找到连续的\r\n\r\n 之后是ASCII码的长度\r\n
					char *lengthPos = strstr(bufRecv, "Content-Length: ");
					if (lengthPos != NULL) {
						downloadsize = atol(lengthPos + strlen("Content-Length: "));
					}

					char *pos = strstr(bufRecv, "\r\n\r\n");
					if (pos != NULL)
					{
						pos += 4;
						if (downloadsize == 0)
						{
							char* pos2 = strstr(pos, "\r\n");
							if (pos2 != NULL)
							{
								*pos2 = '\0';
								downloadsize = strtol(pos, NULL, 16);

								printf("\n download size [%s], long [%ld] \n", pos, downloadsize);

								pos2 += 2;
								len -= (pos2 - bufRecv);

								//需要调整内容
								memcpy(bufRecv, pos2, len);
							}
						}
						else{
							len -= (pos - bufRecv);
							memcpy(bufRecv, pos, len);
						}
						
						process_http_head = true;

						printf("\n downloadsize %ld  \n", downloadsize);

						//超过要下载的大小了， 截断后面的
						if (count + len > downloadsize)
							len = downloadsize - count;

						//不足4096整数，则需要再等
						if (len + index < CHUNK_SIZE)
						{
							index += len;
						//	count += len;
							continue;
						}

						if (callback != NULL)
							callback(bufRecv, index + len, context);

						index = 0;
						
					}

				}
				else {

					//当前下载量
					if (count + len + index > downloadsize)
					{
						len = downloadsize - count - index;
						exit_flag = true;
					}

				
					if (len + index < CHUNK_SIZE)
					{
					//	printf("< CHUNK_SIZE ,  index %d, len %d, count %d  \n", index, len ,  count );

						index += len;
						count += len;
						len = 0;
						continue;
					}

					len += index;
					index = 0;

					if (callback != NULL)
					{
						callback(bufRecv, len, context);
					}
			

				}

				count += len;
				//printf("recv len %d\n", len);
			}
			else
			{
				printf("len error %d \n", len);
			}


		}while (len > 0 && exit_flag == false  );
		
		//printf("break len %d, index %d,  exit_flag %d \n", len, index, exit_flag);


		//不足CHUNK_SIZE的部分
		if (callback != NULL &&  index + len > 0)
		{
			callback(bufRecv,index + len, context);
		}


		free(bufRecv);
		//printf("total recv len %d\n", count);

		// socket环境清理
		closesocket(clientSocket);
		::WSACleanup();
	}
	catch (...)
	{
		printf("%s ret -1 \n", __FUNCTION__);
		return -1;
	}
//	printf("%s ret 0 \n", __FUNCTION__);
	return 0;
}

// Http POST请求
std::string HttpRequest::HttpPost(std::string req, std::string data)
{
	std::string ret = ""; // 返回Http Response
	try
	{
		// 开始进行socket初始化;
		WSADATA wData;
		::WSAStartup(MAKEWORD(2, 2), &wData);

		SOCKET clientSocket = socket(AF_INET, 1, 0);
		struct sockaddr_in ServerAddr = { 0 };
		ServerAddr.sin_addr.s_addr = inet_addr(m_ip.c_str());
		//ServerAddr.sin_addr.s_addr = InetPton(m_ip.c_str());
		ServerAddr.sin_port = htons(m_port);
		ServerAddr.sin_family = AF_INET;
		int errNo = connect(clientSocket, (sockaddr*)&ServerAddr, sizeof(ServerAddr));
		if (errNo == 0)
		{
			// 格式化data长度
			char len[10] = { 0 };
			sprintf(len, "%d", data.length());
			std::string strLen = len;

			std::string strSend = " HTTP/1.1\r\n"
				"Content-Type:application/x-www-form-urlencoded\r\n"
				"Host: " + m_ip + ":" + std::to_string(m_port) + "\r\n"
				"Charset:utf-8\r\n"
				"Content-Length:";
			strSend = "POST " + req + strSend + strLen + "\r\n\r\n" + data;

			// 发送
			errNo = send(clientSocket, strSend.c_str(), strSend.length(), 0);
			if (errNo > 0)
			{
				//cout<<"发送成功\n";
			}
			else
			{
				std::cout << "errNo:" << errNo << std::endl;
				return ret;
			}

			// 接收
			char bufRecv[3069] = { 0 };
			errNo = recv(clientSocket, bufRecv, 3069, 0);
			if (errNo > 0)
			{
				ret = bufRecv;// 如果接收成功，则返回接收的数据内容
			}
			else
			{
				std::cout << "errNo:" << errNo << std::endl;
				return ret;
			}
		}
		else
		{
			errNo = WSAGetLastError();
		}
		// socket环境清理
		::WSACleanup();
	}
	catch (...)
	{
		return "";
	}
	return ret;
}

typedef struct tagHttpPutRequestInfo
{
	std::string  ip;
	std::string req;
	std::string filename;
	int port;
}HttpPutRequestInfo;


//ret 0 成功， -1 失败， 1 重试
int handle_http_put_file(HttpPutRequestInfo *p)
{
	int ret = 0;
	std::string m_ip = p->ip;
	int m_port = p->port;
	std::string req = p->req;
	std::string  filename = p->filename;

	//printf("%s  req  %s , file %s \n", __FUNCTION__, req.c_str(), filename.c_str());
	try
	{

		//发送文件
		FILE* fp;
		fp = fopen(filename.c_str(), "rb");
		int count = 0;
		if (fp == NULL)
		{
			//可能暂时无法打开，正在读写，需要等一会再尝试
			ret = 1;
			return ret;
		}


		// 开始进行socket初始化;
		WSADATA wData;
		::WSAStartup(MAKEWORD(2, 2), &wData);


		SOCKET clientSocket = socket(AF_INET, 1, 0);
		struct sockaddr_in ServerAddr = { 0 };
		ServerAddr.sin_addr.s_addr = inet_addr(m_ip.c_str());
		//ServerAddr.sin_addr.s_addr = InetPton(m_ip.c_str());
		ServerAddr.sin_port = htons(m_port);
		ServerAddr.sin_family = AF_INET;

		int errNo = connect(clientSocket, (sockaddr*)&ServerAddr, sizeof(ServerAddr));
		if (errNo == 0)
		{
			// 格式化data长度
			char len[10] = { 0 };

			struct _stat info;
			_stat(filename.c_str(), &info);

			//检查文件长度
			sprintf(len, "%d", info.st_size);
			std::string strLen = len;
					
			std::string strSend = " HTTP/1.1\r\n"
				"Host: " + m_ip + ":" + std::to_string(m_port) + "\r\n"
				"Content-Type:application/x-www-form-urlencoded;charset=utf-8\r\n"
				"Content-Length: ";
			strSend = "PUT " + req + strSend + strLen + "\r\n\r\n";

			// 发送
			errNo = send(clientSocket, strSend.c_str(), strSend.length(), 0);
			if (errNo > 0)
			{
				if (fp != NULL) {

					char* buf = (char*)malloc(1024 * 1024);
					if (buf != NULL)
					{
						int len = 0;
						do
						{
							len = fread(buf, 1, 1024 * 1024, fp);
						//	printf("fread len %d\n", len);
							if (len > 0)
							{
								count += len;
								errNo = send(clientSocket, buf, len, 0);
								if (errNo < 0)
									break;
							}

						} while (len > 0);

					}
					free(buf);
				}
				else {
					printf("open file  fail!");
					goto exit;
				}

			//	printf("send finish  %s, len %d \n", filename.c_str(), count);
			}
			else
			{
			//	std::cout << "errNo:" << errNo << std::endl;
				goto exit;
			
			}
#if 0
			// 接收
			char bufRecv[3069] = { 0 };
			errNo = recv(clientSocket, bufRecv, 3069, 0);
			if (errNo > 0)
			{
			//	ret = bufRecv;// 如果接收成功，则返回接收的数据内容
			}
#endif
			closesocket(clientSocket);
		}
		else
		{
			errNo = WSAGetLastError();
		}
exit:
		if (fp)
			fclose(fp);

		closesocket(clientSocket);

		// socket环境清理
		::WSACleanup();
		
	}
	catch (...)
	{
		return -1;

	}

	return ret;
}

int  HttpRequest::HttpPutFile(std::string req, std::string filename)
{
	//std::string ret = ""; // 返回Http Response
	//printf("%s  req  %s , file %s \n", __FUNCTION__, req.c_str(), filename.c_str());

	//HANDLE hServieThreadHandle;

	HttpPutRequestInfo  putInfo = { m_ip, req, filename, m_port };

	//后续线程化， 先阻塞
	return handle_http_put_file(&putInfo);

}


#if 0
// 合成JSON字符串 string int 
std::string HttpRequest::genJsonString(std::string key, int value)
{
	char buf[128] = { 0 };
	sprintf(buf, "{\"%s\":%d}", key.c_str(), value);
	std::string ret = buf;
	return ret;
}

// 合成JSON字符串  string  string 
std::string HttpRequest::genJsonString_string(std::map<std::string, std::string> mapRequest)
{
	std::map<std::string, std::string>::iterator iter;
	std::string ret = "{";
	char buf[128] = { 0 };
	for(iter = mapRequest.begin(); iter != mapRequest.end(); ++iter)
	{
		sprintf(buf, "\"%s\":\"%s\"", iter->first.c_str(), iter->second.c_str());
		ret = ret + buf + ",";
		//std::cout << iter->first << ":" << iter->second << std::endl;
	}
	ret = ret.substr(0, ret.length()-1);
	ret = ret + "}";
	/*char buf[128] = { 0 };
	sprintf(buf, "{\"%s\":%s}", key.c_str(), value.c_str());
	
	std::string ret = buf;*/
	std::cout << "json: " << ret << std::endl;
	return ret;
}

// 分割字符串
std::vector<std::string> HttpRequest::split(const std::string &s, const std::string &seperator)
{
	std::vector<std::string> result;
	typedef std::string::size_type string_size;
	string_size i = 0;

	while (i != s.size()) {
		// 找到字符串中首个不等于分隔符的字母
		int flag = 0;
		while (i != s.size() && flag == 0) {
			flag = 1;
			for (string_size x = 0; x < seperator.size(); ++x)
				if (s[i] == seperator[x]) {
					++i;
					flag = 0;
					break;
				}
		}

		// 找到又一个分隔符，将两个分隔符之间的字符串取出
		flag = 0;
		string_size j = i;
		while (j != s.size() && flag == 0) {
			for (string_size x = 0; x < seperator.size(); ++x)
				if (s[j] == seperator[x]) {
					flag = 1;
					break;
				}
			if (flag == 0)
				++j;
		}
		if (i != j) {
			result.push_back(s.substr(i, j - i));
			i = j;
		}
	}
	return result;
}

// 从Response中查找key对应的Header的内容
std::string HttpRequest::getHeader(std::string respose, std::string key)
{
	std::vector<std::string> lines = split(respose, "\r\n");
	std::cout << "lines size: " << lines.size() << std::endl;
	for (int i = 0; i < lines.size(); i++)
	{
		std::vector<std::string> line = split(lines[i], ": ");// 注意空格

		std::cout << "line[0]: " << line[0] << std::endl;
		if (line.size() >= 2 && line[0] == key)
		{
			return line[1];
		}
	}
	return "";
}
#endif